<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Tutorial 10: Shaders</title>
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- Wanted to avoid copying .css to each folder, so copied default .css from doxyen in here, kicked out most stuff we don't need for examples and modified some a little bit. 
     Target was having a single html in each example folder which is created from the main.cpp files and needs no files besides some images below media folder.
     Feel free to improve :)
	 -->
<style>
body, table, div, p, dl {
	font: 400 14px/22px;
}
body {
	background-color: #F0F0F0;
	color: black;
	margin-left: 5%;
	margin-right: 5%;
}
p.reference, p.definition {
	font: 400 14px/22px;
}
.title {
	font: 400 14px/28px;
	font-size: 150%;
	font-weight: bold;
	margin: 10px 2px;
}
h1, h2, h3, h4, h5, h6 {
	-webkit-transition: text-shadow 0.5s linear;
	-moz-transition: text-shadow 0.5s linear;
	-ms-transition: text-shadow 0.5s linear;
	-o-transition: text-shadow 0.5s linear;
	transition: text-shadow 0.5s linear;
	margin-right: 15px;
}
caption {
	font-weight: bold;
}
h3.version {
	font-size: 90%;
	text-align: center;
}
a {
	color: #3D578C;
	font-weight: normal;
	text-decoration: none;
}
.contents a:visited {
	color: #4665A2;
}
a:hover {
	text-decoration: underline;
}
a.el {
	font-weight: bold;
}
a.code, a.code:visited, a.line, a.line:visited {
	color: #4665A2; 
}
a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
	color: #4665A2; 
}
pre.fragment {
	border: 1px solid #C4CFE5;
	background-color: #FBFCFD;
	padding: 4px 6px;
	margin: 4px 8px 4px 2px;
	overflow: auto;
	word-wrap: break-word;
	font-size:  9pt;
	line-height: 125%;
	font-family: monospace, fixed;
	font-size: 105%;
}
div.fragment {
	padding: 0px;
	margin: 4px 8px 4px 2px;
	background-color: #FBFCFD;
	border: 1px solid #C4CFE5;
}
div.line {
	font-family: monospace, fixed;
	font-size: 13px;
	min-height: 13px;
	line-height: 1.0;
	text-wrap: unrestricted;
	white-space: -moz-pre-wrap; /* Moz */
	white-space: -pre-wrap;     /* Opera 4-6 */
	white-space: -o-pre-wrap;   /* Opera 7 */
	white-space: pre-wrap;      /* CSS3  */
	word-wrap: break-word;      /* IE 5.5+ */
	text-indent: -53px;
	padding-left: 53px;
	padding-bottom: 0px;
	margin: 0px;
	-webkit-transition-property: background-color, box-shadow;
	-webkit-transition-duration: 0.5s;
	-moz-transition-property: background-color, box-shadow;
	-moz-transition-duration: 0.5s;
	-ms-transition-property: background-color, box-shadow;
	-ms-transition-duration: 0.5s;
	-o-transition-property: background-color, box-shadow;
	-o-transition-duration: 0.5s;
	transition-property: background-color, box-shadow;
	transition-duration: 0.5s;
}
div.contents {
	margin-top: 10px;
	margin-left: 12px;
	margin-right: 8px;
}
div.center {
	text-align: center;
	margin-top: 0px;
	margin-bottom: 0px;
	padding: 0px;
}
div.center img {
	border: 0px;
}
span.keyword {
	color: #008000
}
span.keywordtype {
	color: #604020
}
span.keywordflow {
	color: #e08000
}
span.comment {
	color: #800000
}
span.preprocessor {
	color: #806020
}
span.stringliteral {
	color: #002080
}
span.charliteral {
	color: #008080
}
blockquote {
	background-color: #F7F8FB;
	border-left: 2px solid #9CAFD4;
	margin: 0 24px 0 4px;
	padding: 0 12px 0 16px;
}
hr {
	height: 0px;
	border: none;
	border-top: 1px solid #4A6AAA;
}
address {
	font-style: normal;
	color: #2A3D61;
}
div.header {
	background-image:url('nav_h.png');
	background-repeat:repeat-x;
	background-color: #F9FAFC;
	margin:  0px;
	border-bottom: 1px solid #C4CFE5;
}
div.headertitle {
	padding: 5px 5px 5px 10px;
}
.image {
	text-align: center;
}
.caption {
	font-weight: bold;
}
div.zoom {
	border: 1px solid #90A5CE;
}
tr.heading h2 {
	margin-top: 12px;
	margin-bottom: 4px;
}
</style>
</head>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--END TITLEAREA-->
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
</div><!-- top -->
<div class="header">
  <div class="headertitle">
<div class="title">Tutorial 10: Shaders </div>  </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><div class="image">
<img src="../../media/example_screenshots/010shot.jpg" alt="010shot.jpg"/>
</div>
 <p>This tutorial shows how to use shaders for D3D9, and OpenGL with the engine and how to create new material types with them. It also shows how to disable the generation of mipmaps at texture loading, and how to use text scene nodes.</p>
<p>This tutorial does not explain how shaders work. I would recommend to read the D3D or OpenGL, documentation, to search a tutorial, or to read a book about this.</p>
<p>At first, we need to include all headers and do the stuff we always do, like in nearly all other tutorials: </p><div class="fragment"><div class="line"><span class="preprocessor">#include &lt;irrlicht.h&gt;</span></div><div class="line"><span class="preprocessor">#include &lt;iostream&gt;</span></div><div class="line"><span class="preprocessor">#include &quot;driverChoice.h&quot;</span></div><div class="line"><span class="preprocessor">#include &quot;exampleHelper.h&quot;</span></div><div class="line"></div><div class="line"><span class="keyword">using namespace </span>irr;</div><div class="line"></div><div class="line"><span class="preprocessor">#ifdef _MSC_VER</span></div><div class="line"><span class="preprocessor">#pragma comment(lib, &quot;Irrlicht.lib&quot;)</span></div><div class="line"><span class="preprocessor">#endif</span></div></div><!-- fragment --><p> Because we want to use some interesting shaders in this tutorials, we need to set some data for them to make them able to compute nice colors. In this example, we'll use a simple vertex shader which will calculate the color of the vertex based on the position of the camera. For this, the shader needs the following data: The inverted world matrix for transforming the normal, the clip matrix for transforming the position, the camera position and the world position of the object for the calculation of the angle of light, and the color of the light. To be able to tell the shader all this data every frame, we have to derive a class from the IShaderConstantSetCallBack interface and override its only method, namely OnSetConstants(). This method will be called every time the material is set. The method setVertexShaderConstant() of the IMaterialRendererServices interface is used to set the data the shader needs. If the user chose to use a High Level shader language like HLSL instead of Assembler in this example, you have to set the variable name as parameter instead of the register index. </p><div class="fragment"><div class="line">IrrlichtDevice* device = 0;</div><div class="line"><span class="keywordtype">bool</span> UseHighLevelShaders = <span class="keyword">false</span>;</div><div class="line"></div><div class="line"><span class="keyword">class </span>MyShaderCallBack : <span class="keyword">public</span> video::IShaderConstantSetCallBack</div><div class="line">{</div><div class="line"><span class="keyword">public</span>:</div><div class="line">    MyShaderCallBack() : WorldViewProjID(-1), TransWorldID(-1), InvWorldID(-1), PositionID(-1),</div><div class="line">                        ColorID(-1), TextureID(-1), FirstUpdate(true)</div><div class="line">    {</div><div class="line">    }</div><div class="line"></div><div class="line">    <span class="keyword">virtual</span> <span class="keywordtype">void</span> OnSetConstants(video::IMaterialRendererServices* services,</div><div class="line">            s32 userData)</div><div class="line">    {</div><div class="line">        video::IVideoDriver* driver = services-&gt;getVideoDriver();</div><div class="line"></div><div class="line">        <span class="comment">// get shader constants id.</span></div><div class="line"></div><div class="line">        <span class="keywordflow">if</span> (UseHighLevelShaders &amp;&amp; FirstUpdate)</div><div class="line">        {</div><div class="line">            WorldViewProjID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;mWorldViewProj&quot;</span>);</div><div class="line">            TransWorldID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;mTransWorld&quot;</span>);</div><div class="line">            InvWorldID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;mInvWorld&quot;</span>);</div><div class="line">            PositionID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;mLightPos&quot;</span>);</div><div class="line">            ColorID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;mLightColor&quot;</span>);</div><div class="line"></div><div class="line">            <span class="comment">// Textures ID are important only for OpenGL interface.</span></div><div class="line"></div><div class="line">            <span class="keywordflow">if</span>(driver-&gt;getDriverType() == video::EDT_OPENGL)</div><div class="line">                TextureID = services-&gt;getVertexShaderConstantID(<span class="stringliteral">&quot;myTexture&quot;</span>);</div><div class="line"></div><div class="line">            FirstUpdate = <span class="keyword">false</span>;</div><div class="line">        }</div><div class="line"></div><div class="line">        <span class="comment">// set inverted world matrix</span></div><div class="line">        <span class="comment">// if we are using highlevel shaders (the user can select this when</span></div><div class="line">        <span class="comment">// starting the program), we must set the constants by name.</span></div><div class="line"></div><div class="line">        core::matrix4 invWorld = driver-&gt;getTransform(video::ETS_WORLD);</div><div class="line">        invWorld.makeInverse();</div><div class="line"></div><div class="line">        <span class="keywordflow">if</span> (UseHighLevelShaders)</div><div class="line">            services-&gt;setVertexShaderConstant(InvWorldID, invWorld.pointer(), 16);</div><div class="line">        <span class="keywordflow">else</span></div><div class="line">            services-&gt;setVertexShaderConstant(invWorld.pointer(), 0, 4);</div><div class="line"></div><div class="line">        <span class="comment">// set clip matrix</span></div><div class="line"></div><div class="line">        core::matrix4 worldViewProj;</div><div class="line">        worldViewProj = driver-&gt;getTransform(video::ETS_PROJECTION);</div><div class="line">        worldViewProj *= driver-&gt;getTransform(video::ETS_VIEW);</div><div class="line">        worldViewProj *= driver-&gt;getTransform(video::ETS_WORLD);</div><div class="line"></div><div class="line">        <span class="keywordflow">if</span> (UseHighLevelShaders)</div><div class="line">            services-&gt;setVertexShaderConstant(WorldViewProjID, worldViewProj.pointer(), 16);</div><div class="line">        <span class="keywordflow">else</span></div><div class="line">            services-&gt;setVertexShaderConstant(worldViewProj.pointer(), 4, 4);</div><div class="line"></div><div class="line">        <span class="comment">// set camera position</span></div><div class="line"></div><div class="line">        core::vector3df pos = device-&gt;getSceneManager()-&gt;</div><div class="line">            getActiveCamera()-&gt;getAbsolutePosition();</div><div class="line"></div><div class="line">        <span class="keywordflow">if</span> (UseHighLevelShaders)</div><div class="line">            services-&gt;setVertexShaderConstant(PositionID, reinterpret_cast&lt;f32*&gt;(&amp;pos), 3);</div><div class="line">        <span class="keywordflow">else</span></div><div class="line">            services-&gt;setVertexShaderConstant(reinterpret_cast&lt;f32*&gt;(&amp;pos), 8, 1);</div><div class="line"></div><div class="line">        <span class="comment">// set light color</span></div><div class="line"></div><div class="line">        video::SColorf col(0.0f,1.0f,1.0f,0.0f);</div><div class="line"></div><div class="line">        <span class="keywordflow">if</span> (UseHighLevelShaders)</div><div class="line">            services-&gt;setVertexShaderConstant(ColorID,</div><div class="line">                    reinterpret_cast&lt;f32*&gt;(&amp;col), 4);</div><div class="line">        <span class="keywordflow">else</span></div><div class="line">            services-&gt;setVertexShaderConstant(reinterpret_cast&lt;f32*&gt;(&amp;col), 9, 1);</div><div class="line"></div><div class="line">        <span class="comment">// set transposed world matrix</span></div><div class="line"></div><div class="line">        core::matrix4 world = driver-&gt;getTransform(video::ETS_WORLD);</div><div class="line">        world = world.getTransposed();</div><div class="line"></div><div class="line">        <span class="keywordflow">if</span> (UseHighLevelShaders)</div><div class="line">        {</div><div class="line">            services-&gt;setVertexShaderConstant(TransWorldID, world.pointer(), 16);</div><div class="line"></div><div class="line">            <span class="comment">// set texture, for textures you can use both an int and a float setPixelShaderConstant interfaces (You need it only for an OpenGL driver).</span></div><div class="line">            s32 TextureLayerID = 0;</div><div class="line">            services-&gt;setPixelShaderConstant(TextureID, &amp;TextureLayerID, 1);</div><div class="line">        }</div><div class="line">        <span class="keywordflow">else</span></div><div class="line">            services-&gt;setVertexShaderConstant(world.pointer(), 10, 4);</div><div class="line">    }</div><div class="line"></div><div class="line"><span class="keyword">private</span>:</div><div class="line">    s32 WorldViewProjID;</div><div class="line">    s32 TransWorldID;</div><div class="line">    s32 InvWorldID;</div><div class="line">    s32 PositionID;</div><div class="line">    s32 ColorID;</div><div class="line">    s32 TextureID;</div><div class="line"></div><div class="line">    <span class="keywordtype">bool</span> FirstUpdate;</div><div class="line">};</div></div><!-- fragment --><p> The next few lines start up the engine just like in most other tutorials before. But in addition, we ask the user if he wants to use high level shaders in this example, if he selected a driver which is capable of doing so. </p><div class="fragment"><div class="line"><span class="keywordtype">int</span> main()</div><div class="line">{</div><div class="line">    <span class="comment">// ask user for driver</span></div><div class="line">    video::E_DRIVER_TYPE driverType=driverChoiceConsole();</div><div class="line">    <span class="keywordflow">if</span> (driverType==video::EDT_COUNT)</div><div class="line">        <span class="keywordflow">return</span> 1;</div><div class="line"></div><div class="line">    <span class="comment">// ask the user if we should use high level shaders for this example</span></div><div class="line">    <span class="keywordflow">if</span> (driverType == video::EDT_DIRECT3D9 ||</div><div class="line">         driverType == video::EDT_OPENGL)</div><div class="line">    {</div><div class="line">        <span class="keywordtype">char</span> i = <span class="charliteral">&#39;y&#39;</span>;</div><div class="line">        printf(<span class="stringliteral">&quot;Please press &#39;y&#39; if you want to use high level shaders.\n&quot;</span>);</div><div class="line">        std::cin &gt;&gt; i;</div><div class="line">        <span class="keywordflow">if</span> (i == <span class="charliteral">&#39;y&#39;</span>)</div><div class="line">        {</div><div class="line">            UseHighLevelShaders = <span class="keyword">true</span>;</div><div class="line">        }</div><div class="line">    }</div><div class="line"></div><div class="line">    <span class="comment">// create device</span></div><div class="line"></div><div class="line">    device = createDevice(driverType, core::dimension2d&lt;u32&gt;(640, 480));</div><div class="line"></div><div class="line">    <span class="keywordflow">if</span> (device == 0)</div><div class="line">        <span class="keywordflow">return</span> 1; <span class="comment">// could not create selected driver.</span></div><div class="line"></div><div class="line"></div><div class="line">    video::IVideoDriver* driver = device-&gt;getVideoDriver();</div><div class="line">    scene::ISceneManager* smgr = device-&gt;getSceneManager();</div><div class="line">    gui::IGUIEnvironment* gui = device-&gt;getGUIEnvironment();</div><div class="line"></div><div class="line">    <span class="keyword">const</span> io::path mediaPath = getExampleMediaPath();</div></div><!-- fragment --><p> Now for the more interesting parts. If we are using Direct3D, we want to load vertex and pixel shader programs, if we have OpenGL, we want to use ARB fragment and vertex programs. I wrote the corresponding programs down into the files d3d9.ps, d3d9.vs, opengl.ps and opengl.vs. We only need the right filenames now. This is done in the following switch. Note, that it is not necessary to write the shaders into text files, like in this example. You can even write the shaders directly as strings into the cpp source file, and use later addShaderMaterial() instead of addShaderMaterialFromFiles(). </p><div class="fragment"><div class="line">io::path vsFileName; <span class="comment">// filename for the vertex shader</span></div><div class="line">io::path psFileName; <span class="comment">// filename for the pixel shader</span></div><div class="line"></div><div class="line"><span class="keywordflow">switch</span>(driverType)</div><div class="line">{</div><div class="line"><span class="keywordflow">case</span> video::EDT_DIRECT3D9:</div><div class="line">    <span class="keywordflow">if</span> (UseHighLevelShaders)</div><div class="line">    {</div><div class="line">        psFileName = mediaPath + <span class="stringliteral">&quot;d3d9.hlsl&quot;</span>;</div><div class="line">        vsFileName = psFileName; <span class="comment">// both shaders are in the same file</span></div><div class="line">    }</div><div class="line">    <span class="keywordflow">else</span></div><div class="line">    {</div><div class="line">        psFileName = mediaPath + <span class="stringliteral">&quot;d3d9.psh&quot;</span>;</div><div class="line">        vsFileName = mediaPath + <span class="stringliteral">&quot;d3d9.vsh&quot;</span>;</div><div class="line">    }</div><div class="line">    <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"><span class="keywordflow">case</span> video::EDT_OPENGL:</div><div class="line">    <span class="keywordflow">if</span> (UseHighLevelShaders)</div><div class="line">    {</div><div class="line">        psFileName = mediaPath + <span class="stringliteral">&quot;opengl.frag&quot;</span>;</div><div class="line">        vsFileName = mediaPath + <span class="stringliteral">&quot;opengl.vert&quot;</span>;</div><div class="line">    }</div><div class="line">    <span class="keywordflow">else</span></div><div class="line">    {</div><div class="line">        psFileName = mediaPath + <span class="stringliteral">&quot;opengl.psh&quot;</span>;</div><div class="line">        vsFileName = mediaPath + <span class="stringliteral">&quot;opengl.vsh&quot;</span>;</div><div class="line">    }</div><div class="line">    <span class="keywordflow">break</span>;</div><div class="line"><span class="keywordflow">default</span>:</div><div class="line">    <span class="keywordflow">break</span>;</div><div class="line">}</div></div><!-- fragment --><p> In addition, we check if the hardware and the selected renderer is capable of executing the shaders we want. If not, we simply set the filename string to 0. This is not necessary, but useful in this example: For example, if the hardware is able to execute vertex shaders but not pixel shaders, we create a new material which only uses the vertex shader, and no pixel shader. Otherwise, if we would tell the engine to create this material and the engine sees that the hardware wouldn't be able to fulfill the request completely, it would not create any new material at all. So in this example you would see at least the vertex shader in action, without the pixel shader. </p><div class="fragment"><div class="line"><span class="keywordflow">if</span> (!driver-&gt;queryFeature(video::EVDF_PIXEL_SHADER_1_1) &amp;&amp;</div><div class="line">    !driver-&gt;queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))</div><div class="line">{</div><div class="line">    device-&gt;getLogger()-&gt;log(<span class="stringliteral">&quot;WARNING: Pixel shaders disabled &quot;</span>\</div><div class="line">        <span class="stringliteral">&quot;because of missing driver/hardware support.&quot;</span>);</div><div class="line">    psFileName = <span class="stringliteral">&quot;&quot;</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keywordflow">if</span> (!driver-&gt;queryFeature(video::EVDF_VERTEX_SHADER_1_1) &amp;&amp;</div><div class="line">    !driver-&gt;queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))</div><div class="line">{</div><div class="line">    device-&gt;getLogger()-&gt;log(<span class="stringliteral">&quot;WARNING: Vertex shaders disabled &quot;</span>\</div><div class="line">        <span class="stringliteral">&quot;because of missing driver/hardware support.&quot;</span>);</div><div class="line">    vsFileName = <span class="stringliteral">&quot;&quot;</span>;</div><div class="line">}</div></div><!-- fragment --><p> Now lets create the new materials. As you maybe know from previous examples, a material type in the Irrlicht engine is set by simply changing the MaterialType value in the SMaterial struct. And this value is just a simple 32 bit value, like video::EMT_SOLID. So we only need the engine to create a new value for us which we can set there. To do this, we get a pointer to the IGPUProgrammingServices and call addShaderMaterialFromFiles(), which returns such a new 32 bit value. That's all.</p>
<p>The parameters to this method are the following: First, the names of the files containing the code of the vertex and the pixel shader. If you would use addShaderMaterial() instead, you would not need file names, then you could write the code of the shader directly as string. The following parameter is a pointer to the IShaderConstantSetCallBack class we wrote at the beginning of this tutorial. If you don't want to set constants, set this to 0. The last parameter tells the engine which material it should use as base material.</p>
<p>To demonstrate this, we create two materials with a different base material, one with EMT_SOLID and one with EMT_TRANSPARENT_ADD_COLOR. </p><div class="fragment"><div class="line"><span class="comment">// create materials</span></div><div class="line"></div><div class="line">video::IGPUProgrammingServices* gpu = driver-&gt;getGPUProgrammingServices();</div><div class="line">s32 newMaterialType1 = 0;</div><div class="line">s32 newMaterialType2 = 0;</div><div class="line"></div><div class="line"><span class="keywordflow">if</span> (gpu)</div><div class="line">{</div></div><!-- fragment --><p> Create one callback instance for each shader material you add. Reason is that the getVertexShaderConstantID returns ID's which are only valid per added material (The ID's tend to be identical as long as the shader code is exactly identical, but it's not good style to depend on that). </p><div class="fragment"><div class="line">    MyShaderCallBack* mcSolid = <span class="keyword">new</span> MyShaderCallBack();</div><div class="line">    MyShaderCallBack* mcTransparentAdd = <span class="keyword">new</span> MyShaderCallBack();</div><div class="line"></div><div class="line">    <span class="comment">// create the shaders depending on if the user wanted high level</span></div><div class="line">    <span class="comment">// or low level shaders:</span></div><div class="line"></div><div class="line">    <span class="keywordflow">if</span> (UseHighLevelShaders)</div><div class="line">    {</div><div class="line">        <span class="comment">// create material from high level shaders (hlsl, glsl)</span></div><div class="line"></div><div class="line">        newMaterialType1 = gpu-&gt;addHighLevelShaderMaterialFromFiles(</div><div class="line">            vsFileName, <span class="stringliteral">&quot;vertexMain&quot;</span>, video::EVST_VS_1_1,</div><div class="line">            psFileName, <span class="stringliteral">&quot;pixelMain&quot;</span>, video::EPST_PS_1_1,</div><div class="line">            mcSolid, video::EMT_SOLID, 0);</div><div class="line"></div><div class="line">        newMaterialType2 = gpu-&gt;addHighLevelShaderMaterialFromFiles(</div><div class="line">            vsFileName, <span class="stringliteral">&quot;vertexMain&quot;</span>, video::EVST_VS_1_1,</div><div class="line">            psFileName, <span class="stringliteral">&quot;pixelMain&quot;</span>, video::EPST_PS_1_1,</div><div class="line">            mcTransparentAdd, video::EMT_TRANSPARENT_ADD_COLOR, 0);</div><div class="line">    }</div><div class="line">    <span class="keywordflow">else</span></div><div class="line">    {</div><div class="line">        <span class="comment">// create material from low level shaders (asm or arb_asm)</span></div><div class="line"></div><div class="line">        newMaterialType1 = gpu-&gt;addShaderMaterialFromFiles(vsFileName,</div><div class="line">            psFileName, mcSolid, video::EMT_SOLID);</div><div class="line"></div><div class="line">        newMaterialType2 = gpu-&gt;addShaderMaterialFromFiles(vsFileName,</div><div class="line">            psFileName, mcTransparentAdd, video::EMT_TRANSPARENT_ADD_COLOR);</div><div class="line">    }</div><div class="line"></div><div class="line">    mcSolid-&gt;drop();</div><div class="line">    mcTransparentAdd-&gt;drop();</div><div class="line">}</div></div><!-- fragment --><p> Now it's time for testing the materials. We create a test cube and set the material we created. In addition, we add a text scene node to the cube and a rotation animator to make it look more interesting and important. </p><div class="fragment"><div class="line"><span class="comment">// create test scene node 1, with the new created material type 1</span></div><div class="line"></div><div class="line">scene::ISceneNode* node = smgr-&gt;addCubeSceneNode(50);</div><div class="line">node-&gt;setPosition(core::vector3df(0,0,0));</div><div class="line">node-&gt;setMaterialTexture(0, driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;wall.bmp&quot;</span>));</div><div class="line">node-&gt;setMaterialFlag(video::EMF_LIGHTING, <span class="keyword">false</span>);</div><div class="line">node-&gt;setMaterialType((video::E_MATERIAL_TYPE)newMaterialType1);</div><div class="line"></div><div class="line">smgr-&gt;addTextSceneNode(gui-&gt;getBuiltInFont(),</div><div class="line">        L<span class="stringliteral">&quot;PS &amp; VS &amp; EMT_SOLID&quot;</span>,</div><div class="line">        video::SColor(255,255,255,255), node);</div><div class="line"></div><div class="line">scene::ISceneNodeAnimator* anim = smgr-&gt;createRotationAnimator(</div><div class="line">        core::vector3df(0,0.3f,0));</div><div class="line">node-&gt;addAnimator(anim);</div><div class="line">anim-&gt;drop();</div></div><!-- fragment --><p> Same for the second cube, but with the second material we created. </p><div class="fragment"><div class="line"><span class="comment">// create test scene node 2, with the new created material type 2</span></div><div class="line"></div><div class="line">node = smgr-&gt;addCubeSceneNode(50);</div><div class="line">node-&gt;setPosition(core::vector3df(0,-10,50));</div><div class="line">node-&gt;setMaterialTexture(0, driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;wall.bmp&quot;</span>));</div><div class="line">node-&gt;setMaterialFlag(video::EMF_LIGHTING, <span class="keyword">false</span>);</div><div class="line">node-&gt;setMaterialFlag(video::EMF_BLEND_OPERATION, <span class="keyword">true</span>);</div><div class="line">node-&gt;setMaterialType((video::E_MATERIAL_TYPE)newMaterialType2);</div><div class="line"></div><div class="line">smgr-&gt;addTextSceneNode(gui-&gt;getBuiltInFont(),</div><div class="line">        L<span class="stringliteral">&quot;PS &amp; VS &amp; EMT_TRANSPARENT&quot;</span>,</div><div class="line">        video::SColor(255,255,255,255), node);</div><div class="line"></div><div class="line">anim = smgr-&gt;createRotationAnimator(core::vector3df(0,0.3f,0));</div><div class="line">node-&gt;addAnimator(anim);</div><div class="line">anim-&gt;drop();</div></div><!-- fragment --><p> Then we add a third cube without a shader on it, to be able to compare the cubes. </p><div class="fragment"><div class="line"><span class="comment">// add a scene node with no shader</span></div><div class="line"></div><div class="line">node = smgr-&gt;addCubeSceneNode(50);</div><div class="line">node-&gt;setPosition(core::vector3df(0,50,25));</div><div class="line">node-&gt;setMaterialTexture(0, driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;wall.bmp&quot;</span>));</div><div class="line">node-&gt;setMaterialFlag(video::EMF_LIGHTING, <span class="keyword">false</span>);</div><div class="line">smgr-&gt;addTextSceneNode(gui-&gt;getBuiltInFont(), L<span class="stringliteral">&quot;NO SHADER&quot;</span>,</div><div class="line">    video::SColor(255,255,255,255), node);</div></div><!-- fragment --><p> And last, we add a skybox and a user controlled camera to the scene. For the skybox textures, we disable mipmap generation, because we don't need mipmaps on it. </p><div class="fragment"><div class="line"><span class="comment">// add a nice skybox</span></div><div class="line"></div><div class="line">driver-&gt;setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, <span class="keyword">false</span>);</div><div class="line"></div><div class="line">smgr-&gt;addSkyBoxSceneNode(</div><div class="line">    driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;irrlicht2_up.jpg&quot;</span>),</div><div class="line">    driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;irrlicht2_dn.jpg&quot;</span>),</div><div class="line">    driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;irrlicht2_lf.jpg&quot;</span>),</div><div class="line">    driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;irrlicht2_rt.jpg&quot;</span>),</div><div class="line">    driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;irrlicht2_ft.jpg&quot;</span>),</div><div class="line">    driver-&gt;getTexture(mediaPath + <span class="stringliteral">&quot;irrlicht2_bk.jpg&quot;</span>));</div><div class="line"></div><div class="line">driver-&gt;setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, <span class="keyword">true</span>);</div><div class="line"></div><div class="line"><span class="comment">// add a camera and disable the mouse cursor</span></div><div class="line"></div><div class="line">scene::ICameraSceneNode* cam = smgr-&gt;addCameraSceneNodeFPS();</div><div class="line">cam-&gt;setPosition(core::vector3df(-100,50,100));</div><div class="line">cam-&gt;setTarget(core::vector3df(0,0,0));</div><div class="line">device-&gt;getCursorControl()-&gt;setVisible(<span class="keyword">false</span>);</div></div><!-- fragment --><p> Now draw everything. That's all. </p><div class="fragment"><div class="line">    <span class="keywordtype">int</span> lastFPS = -1;</div><div class="line"></div><div class="line">    <span class="keywordflow">while</span>(device-&gt;run())</div><div class="line">        <span class="keywordflow">if</span> (device-&gt;isWindowActive())</div><div class="line">    {</div><div class="line">        driver-&gt;beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(255,0,0,0));</div><div class="line">        smgr-&gt;drawAll();</div><div class="line">        driver-&gt;endScene();</div><div class="line"></div><div class="line">        <span class="keywordtype">int</span> fps = driver-&gt;getFPS();</div><div class="line"></div><div class="line">        <span class="keywordflow">if</span> (lastFPS != fps)</div><div class="line">        {</div><div class="line">            core::stringw str = L<span class="stringliteral">&quot;Irrlicht Engine - Vertex and pixel shader example [&quot;</span>;</div><div class="line">            str += driver-&gt;getName();</div><div class="line">            str += <span class="stringliteral">&quot;] FPS:&quot;</span>;</div><div class="line">            str += fps;</div><div class="line"></div><div class="line">            device-&gt;setWindowCaption(str.c_str());</div><div class="line">            lastFPS = fps;</div><div class="line">        }</div><div class="line">    }</div><div class="line"></div><div class="line">    device-&gt;drop();</div><div class="line"></div><div class="line">    <span class="keywordflow">return</span> 0;</div><div class="line">}</div></div><!-- fragment --><p> Compile and run this, and I hope you have fun with your new little shader writing tool :). </p>
</div></div><!-- contents -->
<!-- HTML footer for doxygen 1.8.13-->
<!-- start footer part -->
<p>&nbsp;</p>
</body>
</html>
